package controller;

import java.awt.TrayIcon.MessageType;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.util.LinkedList;
import java.util.Queue;

import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;

import client.Client;

import common.Constants;

class Receiver implements Runnable {

	// back pointer to the controller that manages this receiver thread
	private Controller theController;

	private Queue<Message> messages;

	// max message size
	private int messageMaxSize;

	// socket to receive messages
	private DatagramSocket socket;

	// Receiver thread
	private Thread aThread;

	private static Logger logger = null;

	public Receiver(DatagramSocket socket, int messageMaxSize,
			Controller theController) throws SocketException {
		Receiver.logger = Logger.getLogger(Controller.class);
		PropertyConfigurator.configure(Constants.LOG_CONFIG);

		this.messageMaxSize = messageMaxSize;
		this.socket = socket;
		this.messages = new LinkedList<Message>();
		this.theController = theController;
		this.aThread = new Thread(this);
		this.aThread.setName("ReceiverThread");
		this.aThread.start();

	}

	// queues a message into the message queue
	public boolean queueMessage(Message msg) {

		// sychronized block to prevent concurrent read/write to the queue
		synchronized (messages) {
			logger.info("Queued self message in the Receiver");
			this.messages.add(msg);
		}
		return true;
	}

	@Override
	public void run() {
		logger.info("Receiver thread started");

		// create an array of bytes to receive message
		byte[] dataToBeReceived = new byte[this.messageMaxSize];

		while (true) {

			// prepare a place for incoming packet
			DatagramPacket aPacket = new DatagramPacket(dataToBeReceived,
					dataToBeReceived.length);

			// receive a packet
			try {
				socket.receive(aPacket);
			} catch (IOException e) {
				e.printStackTrace();
			}

			Message receivedMessage = new Message(aPacket.getData());
			logger.info("Received from the router : \n"
					+ receivedMessage.toString());
			Constants.MessageType msgType = receivedMessage.getMessageType();

			switch (msgType) {
			case INSERT:
			case DELETE:

				// what if the leader is dead, and the router returns the msg ?
				int source_id = receivedMessage.getSourceId();
				if (source_id == Constants.ROUTER_ID) {
					this.theController.becomeLeader(receivedMessage);
				} else {
					logger.info("Placing insert/delete request in controller's dispatch queue");
					this.theController.queueMessage(receivedMessage);
				}
				break;
			case PREPARE:
				logger.info("Attempting handlePrepareMsg");
				this.theController.handlePrepareMsg(receivedMessage);
				break;
			case ACK:
				logger.info("Attempting handleAckMsg");
				this.theController.handleAckMsg(receivedMessage);
				break;
			case ACCEPT:
				logger.info("Attempting handleAcceptMsg");
				this.theController.handleAcceptMsg(receivedMessage);
				break;
			case DECIDE:
				logger.info("Attempting handleDecideMsg");
				this.theController.handleDecideMsg(receivedMessage);
				break;
			}

		}

	}

}